package com.gjs.cd.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gjs.cd.mapper.CdCodeGenerateBatchMapper;
import com.gjs.cd.pojo.dto.TotalReq;
import com.gjs.cd.pojo.entity.CdCodeGenerateBatch;
import com.gjs.cd.pojo.entity.CdCodeValidTerm;
import com.gjs.cd.pojo.entity.CdYearSubjectCode;
import com.gjs.cd.pojo.vo.BatchVo;
import com.gjs.cd.service.*;
import com.gjs.common.enums.entity.QuarterEnum;
import com.gjs.common.microservice.api.IResourceMicroService;
import com.gjs.common.pojo.dto.request.PageQuery;
import com.gjs.common.pojo.dto.resource.TypeDTO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zhongwenguang
 * @date 2022-3-16 11:52
 */
@Service
public class CdCodeGenerateBatchServiceImpl extends ServiceImpl<CdCodeGenerateBatchMapper, CdCodeGenerateBatch> implements ICdCodeGenerateBatchService {
    @Autowired
    private CdCodeGenerateBatchMapper cdCodeGenerateBatchMapper;

    @Autowired
    private ICdCodeValidTermService iCdCodeValidTermService;

    @Autowired
    private ICdDetailService iCdDetailService;

    @Autowired
    private ICdYearSubjectCodeService iCdYearSubjectCodeService;

    @Autowired
    private ICommonService iCommonService;

   @Autowired
    private ICdCodeGenerateBatchService iCdCodeGenerateBatchService;

    @DubboReference
    private IResourceMicroService iResourceMicroService;

    @Autowired
    private ICodeService iCodeService;

    @Override
    public List<TotalReq> getTotalByIds(List<Long> cdIds) {
        List<TotalReq> list = cdCodeGenerateBatchMapper.getTotalByIdList(cdIds);
        return list;
    }

    @Override
    public IPage<BatchVo> getBatchList(PageQuery pageQuery, Long cdId, Long cdCodeValidTermId) {
        LambdaQueryWrapper<CdCodeGenerateBatch> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(CdCodeGenerateBatch::getCdId,cdId);
        lambdaQueryWrapper.eq(cdCodeValidTermId != 0,CdCodeGenerateBatch::getValidTermId,cdCodeValidTermId);
        IPage<CdCodeGenerateBatch> cdCodeGenerateBatchIPage = this.page(pageQuery.buildPage(),lambdaQueryWrapper);
        Integer num = cdCodeGenerateBatchIPage.getRecords().size();
        IPage<BatchVo> batchVoIPage = null;
        if (num>0){
            //获取
            List<Long> cdCodeValidTermIdList = new ArrayList<>();
            for (int i=0;i<num;i++){
                cdCodeValidTermIdList.add(cdCodeGenerateBatchIPage.getRecords().get(i).getValidTermId());
            }
            LambdaQueryWrapper<CdCodeValidTerm> codeValidTermLambdaQueryWrapper = new LambdaQueryWrapper<>();
            codeValidTermLambdaQueryWrapper.in(CdCodeValidTerm::getId,cdCodeValidTermIdList);
            List<CdCodeValidTerm> cdCodeValidTerms = iCdCodeValidTermService.list(codeValidTermLambdaQueryWrapper);
            Map<Long, LocalDateTime> expireTimeMap = cdCodeValidTerms.stream().collect(Collectors.toMap(CdCodeValidTerm::getId,CdCodeValidTerm::getExpireTime));
            Map<Long, Integer> codeYearMap = cdCodeValidTerms.stream().collect(Collectors.toMap(CdCodeValidTerm::getId,CdCodeValidTerm::getCodeYear));
            Map<Long, QuarterEnum> quarterMap = cdCodeValidTerms.stream().collect(Collectors.toMap(CdCodeValidTerm::getId,CdCodeValidTerm::getCodeQuarter));
            batchVoIPage = cdCodeGenerateBatchIPage.convert(u->{
                BatchVo batchVo = new BatchVo();
                BeanUtil.copyProperties(u,batchVo);
//                batchVo.setId(u.getId());
//                batchVo.setBatchName(u.getBatchName());
//                batchVo.setCreateTime(u.getCreateTime());
//                batchVo.setTargetNum(u.getTargetNum());
//                batchVo.setRealNum(u.getRealNum());
//                batchVo.setStatus(u.getStatus());
                batchVo.setCodeYear(codeYearMap.get(u.getValidTermId()));
                batchVo.setQuarter(quarterMap.get(u.getValidTermId()));
                batchVo.setExpireTime(expireTimeMap.get(u.getValidTermId()));
                return batchVo;
            });
        }
        return batchVoIPage;
    }

    @Override
    public Boolean addBatch(CdCodeGenerateBatch cdCodeGenerateBatch) {

        /**
         * 基本逻辑：
         * 1.首先判断是否需要新建表，建表条件有两个：
         * （1）没有对应学科的激活码表；
         * （2）有对应学科的激活码表，但是该表的激活码数量与本次生成的激活数量（cdCodeGenerateBatch.getTargetNum()）之和大于500万时需要新建表
         * 如何判断：获取该学科激活码表名后缀，生成表名并判断
         * （1）首先通过学科id在CdYearSubjectCode表中查询subject_code字段，如果为空则表名为z开头，如果为非空则取实际值；
         * （2）获取当前批次的年份，并截取后两位，如2022取值为22
         * （2）拼接表名后缀，鉴于当前单个学科激活码一般不超过一千万的实际情况，所以单个学科一般不会大于两张表，即前表名后缀可以表示为:学科代码+1+年份，如:z122
         * 2.生成激活码并批量插入激活码：
         * （1）单次生成插入的数据不大于30000条，大于则需要分批插入。
         * （2）考虑到生成激活码和插入激活码是耗时操作所以这部分需要通过新线程来执行，所以可以先返回分批插入前的执行结果给前端。
         * （3）执行结果分为三种：失败、部分成功、全部成功，对应的status值分别为0、1、2，并在数据库中的real_num字段中记录实际插入的数据。
         * （4）前端用户主动刷新获取新增激活码的结果。
         */
        Long cdId = cdCodeGenerateBatch.getCdId();
        //通过cdId获取subjectId
        Long subjectId = iCdDetailService.getById(cdId).getSubjectId();
        //判断当前学科是否设置年度学科代码，并生成表名后面部分
        Long validTermId = cdCodeGenerateBatch.getValidTermId();//获取当前批次validTermId
        LambdaQueryWrapper<CdYearSubjectCode> cdYearSubjectCodeLambdaQueryWrapper = new LambdaQueryWrapper<>();
        cdYearSubjectCodeLambdaQueryWrapper.eq(CdYearSubjectCode::getSubjectId,subjectId);
        //cdYearSubjectCodeLambdaQueryWrapper.eq(CdYearSubjectCode::getValidTermId,validTermId);//取消年度学科代码表与年度的关联
        CdYearSubjectCode cdYearSubjectCode = iCdYearSubjectCodeService.getOne(cdYearSubjectCodeLambdaQueryWrapper);//查询年度代码表

        Integer codeYear = iCdCodeValidTermService.getById(validTermId).getCodeYear();//获取年份
        String simpleYear = codeYear.toString().substring(2,4);//获取年份后两位
        String subjectCode = cdYearSubjectCode.getSubjectCode();
        String tablePre = cdYearSubjectCode == null ? ("z1"+simpleYear) : (subjectCode + "1"+simpleYear);//生成表名后缀部分
        //判断表是否存在
        String tableName = "cd_code_"+tablePre;
        int isExistTable = iCommonService.isTableExist(tableName);
        if (isExistTable == 0){//不存在表的情况
            //获取学科名称
            String subjectName = getSubjectName(subjectId);
            //创建cd_code表，激活码表
            iCommonService.createCdCode(tableName,subjectName);
            //创建cd_code_record表，激活码记录表
            iCommonService.createCdCodeRecord("cd_code_activation_record_"+tablePre,subjectName);
        }else{//存在表的情况，考虑数量书否大于500万
            int codeTotol = iCommonService.queryCodeTotal(tableName) + cdCodeGenerateBatch.getTargetNum();//当前数据库中的数量
            //此情况为不存在该表需要创建
            if (codeTotol>5000000){
                tablePre = subjectCode + "2"+simpleYear; //大于500万时需要生成新的表名后缀
                tableName = "cd_code_"+tablePre;
                //获取学科名称
                String subjectName = getSubjectName(subjectId);
                //创建cd_code表，激活码表
                iCommonService.createCdCode(tableName,subjectName);
                //创建cd_code_record表，激活码记录表
                iCommonService.createCdCodeRecord("cd_code_activation_record_"+tablePre,subjectName);
            }
        }
        //向批次表中插入数据
        cdCodeGenerateBatch.setPreTableName(tablePre);
        cdCodeGenerateBatch.setStatus(0);
        iCdCodeGenerateBatchService.save(cdCodeGenerateBatch);
        //生成激活码,超时操作采用新建线程方式
        MakeCodeThread makeCodeThread = new MakeCodeThread(cdCodeGenerateBatch.getId(),cdCodeGenerateBatch.getTargetNum());
        makeCodeThread.run();
//        iCodeService.add(cdCodeGenerateBatch.getId(),cdCodeGenerateBatch.getTargetNum());
        return true;
    }



    //获取学科名称
    private String getSubjectName(Long subjectId){
        List<TypeDTO> subjectList = iResourceMicroService.listSubjects();
        for (int i=0;i<subjectList.size();i++){
            if (subjectList.get(i).getKey()==subjectId){
                return subjectList.get(i).getText();
            }
        }
        return null;
    }


    class MakeCodeThread extends Thread{
        private Long cdCodeGenerateBatchId;
        private Integer cdCodeGenerateBatchTargetNum;
        public MakeCodeThread(Long cdCodeGenerateBatchId, Integer cdCodeGenerateBatchTargetNum) {
            this.cdCodeGenerateBatchId = cdCodeGenerateBatchId;
            this.cdCodeGenerateBatchTargetNum = cdCodeGenerateBatchTargetNum;
        }
        @Override
        public void run() {
            super.run();
            iCodeService.add(cdCodeGenerateBatchId,cdCodeGenerateBatchTargetNum);
        }
    }
}
